// $Id: //info.ravenbrook.com/project/ccc/master/tool/grid.js#1 $
//
// JavaScript to display the equal area grid.
//
// REFERENCES
//
// [GL1.1] "The OpenGL Graphics System: A Specification (Version 1.1)";
//   Mark Segal, Kurt Akeley; Silicon Graphics, Inc.; 1997-03-04;
//   http://www.opengl.org/documentation/specs/version1.1/GLspec1.1.ps
  
// Maps function f over array a, returning an array of f's results.
// Method Ready: If a is missing (undefined) then uses this instead.
function map(f, a)
{
  if(a === undefined) {
    a = this
  }
  var r = []
  for(var i=0; i < a.length; ++i) {
    r.push(f(a[i]))
  }
  return r
}


grid=[[[3.7491518045553436e-33, -6.1230317691118863e-17, 1.0],
[-6.1230317691118863e-17, -7.4983036091106872e-33, 1.0],
[-0.43588989435406733, -5.3379353419295565e-17, 0.90000000000000002],
[2.6689676709647783e-17, -0.43588989435406733, 0.90000000000000002]],
[[6.1230317691118863e-17, 0.0, 1.0], [3.7491518045553436e-33,
-6.1230317691118863e-17, 1.0], [2.6689676709647783e-17,
-0.43588989435406733, 0.90000000000000002], [0.43588989435406733, 0.0,
0.90000000000000002]], [[3.7491518045553436e-33, 6.1230317691118863e-17,
1.0], [6.1230317691118863e-17, 0.0, 1.0], [0.43588989435406733, 0.0,
0.90000000000000002], [2.6689676709647783e-17, 0.43588989435406733,
0.90000000000000002]], [[-6.1230317691118863e-17,
7.4983036091106872e-33, 1.0], [3.7491518045553436e-33,
6.1230317691118863e-17, 1.0], [2.6689676709647783e-17,
0.43588989435406733, 0.90000000000000002], [-0.43588989435406733,
5.3379353419295565e-17, 0.90000000000000002]], [[-0.30822070014844877,
-0.30822070014844882, 0.90000000000000002], [-0.43588989435406733,
-5.3379353419295565e-17, 0.90000000000000002], [-0.71414284285428486,
-8.7454386289613274e-17, 0.70000000000000007], [-0.5049752469181038,
-0.5049752469181038, 0.70000000000000007]], [[2.6689676709647783e-17,
-0.43588989435406733, 0.90000000000000002], [-0.30822070014844877,
-0.30822070014844882, 0.90000000000000002], [-0.5049752469181038,
-0.5049752469181038, 0.70000000000000007], [4.3727193144806637e-17,
-0.71414284285428486, 0.70000000000000007]], [[0.30822070014844882,
-0.30822070014844877, 0.90000000000000002], [2.6689676709647783e-17,
-0.43588989435406733, 0.90000000000000002], [4.3727193144806637e-17,
-0.71414284285428486, 0.70000000000000007], [0.5049752469181038,
-0.5049752469181038, 0.70000000000000007]], [[0.43588989435406733, 0.0,
0.90000000000000002], [0.30822070014844882, -0.30822070014844877,
0.90000000000000002], [0.5049752469181038, -0.5049752469181038,
0.70000000000000007], [0.71414284285428486, 0.0, 0.70000000000000007]],
[[0.30822070014844882, 0.30822070014844877, 0.90000000000000002],
[0.43588989435406733, 0.0, 0.90000000000000002], [0.71414284285428486,
0.0, 0.70000000000000007], [0.5049752469181038, 0.5049752469181038,
0.70000000000000007]], [[2.6689676709647783e-17, 0.43588989435406733,
0.90000000000000002], [0.30822070014844882, 0.30822070014844877,
0.90000000000000002], [0.5049752469181038, 0.5049752469181038,
0.70000000000000007], [4.3727193144806637e-17, 0.71414284285428486,
0.70000000000000007]], [[-0.30822070014844877, 0.30822070014844882,
0.90000000000000002], [2.6689676709647783e-17, 0.43588989435406733,
0.90000000000000002], [4.3727193144806637e-17, 0.71414284285428486,
0.70000000000000007], [-0.5049752469181038, 0.5049752469181038,
0.70000000000000007]], [[-0.43588989435406733, 5.3379353419295565e-17,
0.90000000000000002], [-0.30822070014844877, 0.30822070014844882,
0.90000000000000002], [-0.5049752469181038, 0.5049752469181038,
0.70000000000000007], [-0.71414284285428486, 8.7454386289613274e-17,
0.70000000000000007]], [[-0.61846584384264902, -0.35707142142714238,
0.70000000000000007], [-0.71414284285428486, -8.7454386289613274e-17,
0.70000000000000007], [-0.91651513899116799, -1.1223702625829834e-16,
0.39999999999999997], [-0.79372539331937719, -0.45825756949558394,
0.39999999999999997]], [[-0.35707142142714227, -0.61846584384264902,
0.70000000000000007], [-0.61846584384264902, -0.35707142142714238,
0.70000000000000007], [-0.79372539331937719, -0.45825756949558394,
0.39999999999999997], [-0.45825756949558377, -0.79372539331937719,
0.39999999999999997]], [[4.3727193144806637e-17, -0.71414284285428486,
0.70000000000000007], [-0.35707142142714227, -0.61846584384264902,
0.70000000000000007], [-0.45825756949558377, -0.79372539331937719,
0.39999999999999997], [5.6118513129149171e-17, -0.91651513899116799,
0.39999999999999997]], [[0.35707142142714249, -0.61846584384264891,
0.70000000000000007], [4.3727193144806637e-17, -0.71414284285428486,
0.70000000000000007], [5.6118513129149171e-17, -0.91651513899116799,
0.39999999999999997], [0.45825756949558411, -0.79372539331937708,
0.39999999999999997]], [[0.61846584384264902, -0.35707142142714238,
0.70000000000000007], [0.35707142142714249, -0.61846584384264891,
0.70000000000000007], [0.45825756949558411, -0.79372539331937708,
0.39999999999999997], [0.79372539331937719, -0.45825756949558394,
0.39999999999999997]], [[0.71414284285428486, 0.0, 0.70000000000000007],
[0.61846584384264902, -0.35707142142714238, 0.70000000000000007],
[0.79372539331937719, -0.45825756949558394, 0.39999999999999997],
[0.91651513899116799, 0.0, 0.39999999999999997]], [[0.61846584384264902,
0.35707142142714238, 0.70000000000000007], [0.71414284285428486, 0.0,
0.70000000000000007], [0.91651513899116799, 0.0, 0.39999999999999997],
[0.79372539331937719, 0.45825756949558394, 0.39999999999999997]],
[[0.35707142142714249, 0.61846584384264891, 0.70000000000000007],
[0.61846584384264902, 0.35707142142714238, 0.70000000000000007],
[0.79372539331937719, 0.45825756949558394, 0.39999999999999997],
[0.45825756949558411, 0.79372539331937708, 0.39999999999999997]],
[[4.3727193144806637e-17, 0.71414284285428486, 0.70000000000000007],
[0.35707142142714249, 0.61846584384264891, 0.70000000000000007],
[0.45825756949558411, 0.79372539331937708, 0.39999999999999997],
[5.6118513129149171e-17, 0.91651513899116799, 0.39999999999999997]],
[[-0.35707142142714227, 0.61846584384264902, 0.70000000000000007],
[4.3727193144806637e-17, 0.71414284285428486, 0.70000000000000007],
[5.6118513129149171e-17, 0.91651513899116799, 0.39999999999999997],
[-0.45825756949558377, 0.79372539331937719, 0.39999999999999997]],
[[-0.61846584384264902, 0.35707142142714238, 0.70000000000000007],
[-0.35707142142714227, 0.61846584384264902, 0.70000000000000007],
[-0.45825756949558377, 0.79372539331937719, 0.39999999999999997],
[-0.79372539331937719, 0.45825756949558394, 0.39999999999999997]],
[[-0.71414284285428486, 8.7454386289613274e-17, 0.70000000000000007],
[-0.61846584384264902, 0.35707142142714238, 0.70000000000000007],
[-0.79372539331937719, 0.45825756949558394, 0.39999999999999997],
[-0.91651513899116799, 1.1223702625829834e-16, 0.39999999999999997]],
[[-0.84674957815067731, -0.35073515920370762, 0.39999999999999997],
[-0.91651513899116799, -1.1223702625829834e-16, 0.39999999999999997],
[-1.0, -1.2246063538223773e-16, 0.0], [-0.92387953251128674,
-0.38268343236508989, 0.0]], [[-0.64807406984078597,
-0.64807406984078608, 0.39999999999999997], [-0.84674957815067731,
-0.35073515920370762, 0.39999999999999997], [-0.92387953251128674,
-0.38268343236508989, 0.0], [-0.70710678118654746, -0.70710678118654757,
0.0]], [[-0.35073515920370746, -0.84674957815067731,
0.39999999999999997], [-0.64807406984078597, -0.64807406984078608,
0.39999999999999997], [-0.70710678118654746, -0.70710678118654757, 0.0],
[-0.38268343236508973, -0.92387953251128674, 0.0]],
[[5.6118513129149171e-17, -0.91651513899116799, 0.39999999999999997],
[-0.35073515920370746, -0.84674957815067731, 0.39999999999999997],
[-0.38268343236508973, -0.92387953251128674, 0.0],
[6.1230317691118863e-17, -1.0, 0.0]], [[0.35073515920370757,
-0.84674957815067731, 0.39999999999999997], [5.6118513129149171e-17,
-0.91651513899116799, 0.39999999999999997], [6.1230317691118863e-17,
-1.0, 0.0], [0.38268343236508984, -0.92387953251128674, 0.0]],
[[0.64807406984078608, -0.64807406984078597, 0.39999999999999997],
[0.35073515920370757, -0.84674957815067731, 0.39999999999999997],
[0.38268343236508984, -0.92387953251128674, 0.0], [0.70710678118654757,
-0.70710678118654746, 0.0]], [[0.84674957815067731,
-0.35073515920370751, 0.39999999999999997], [0.64807406984078608,
-0.64807406984078597, 0.39999999999999997], [0.70710678118654757,
-0.70710678118654746, 0.0], [0.92387953251128674, -0.38268343236508978,
0.0]], [[0.91651513899116799, 0.0, 0.39999999999999997],
[0.84674957815067731, -0.35073515920370751, 0.39999999999999997],
[0.92387953251128674, -0.38268343236508978, 0.0], [1.0, 0.0, 0.0]],
[[0.84674957815067731, 0.35073515920370751, 0.39999999999999997],
[0.91651513899116799, 0.0, 0.39999999999999997], [1.0, 0.0, 0.0],
[0.92387953251128674, 0.38268343236508978, 0.0]], [[0.64807406984078608,
0.64807406984078597, 0.39999999999999997], [0.84674957815067731,
0.35073515920370751, 0.39999999999999997], [0.92387953251128674,
0.38268343236508978, 0.0], [0.70710678118654757, 0.70710678118654746,
0.0]], [[0.35073515920370757, 0.84674957815067731, 0.39999999999999997],
[0.64807406984078608, 0.64807406984078597, 0.39999999999999997],
[0.70710678118654757, 0.70710678118654746, 0.0], [0.38268343236508984,
0.92387953251128674, 0.0]], [[5.6118513129149171e-17,
0.91651513899116799, 0.39999999999999997], [0.35073515920370757,
0.84674957815067731, 0.39999999999999997], [0.38268343236508984,
0.92387953251128674, 0.0], [6.1230317691118863e-17, 1.0, 0.0]],
[[-0.35073515920370746, 0.84674957815067731, 0.39999999999999997],
[5.6118513129149171e-17, 0.91651513899116799, 0.39999999999999997],
[6.1230317691118863e-17, 1.0, 0.0], [-0.38268343236508973,
0.92387953251128674, 0.0]], [[-0.64807406984078597, 0.64807406984078608,
0.39999999999999997], [-0.35073515920370746, 0.84674957815067731,
0.39999999999999997], [-0.38268343236508973, 0.92387953251128674, 0.0],
[-0.70710678118654746, 0.70710678118654757, 0.0]],
[[-0.84674957815067731, 0.35073515920370762, 0.39999999999999997],
[-0.64807406984078597, 0.64807406984078608, 0.39999999999999997],
[-0.70710678118654746, 0.70710678118654757, 0.0], [-0.92387953251128674,
0.38268343236508989, 0.0]], [[-0.91651513899116799,
1.1223702625829834e-16, 0.39999999999999997], [-0.84674957815067731,
0.35073515920370762, 0.39999999999999997], [-0.92387953251128674,
0.38268343236508989, 0.0], [-1.0, 1.2246063538223773e-16, 0.0]], [[-1.0,
1.2246063538223773e-16, -0.0], [-0.92387953251128674,
0.38268343236508989, -0.0], [-0.84674957815067731, 0.35073515920370762,
-0.39999999999999997], [-0.91651513899116799, 1.1223702625829834e-16,
-0.39999999999999997]], [[-0.92387953251128674, 0.38268343236508989,
-0.0], [-0.70710678118654746, 0.70710678118654757, -0.0],
[-0.64807406984078597, 0.64807406984078608, -0.39999999999999997],
[-0.84674957815067731, 0.35073515920370762, -0.39999999999999997]],
[[-0.70710678118654746, 0.70710678118654757, -0.0],
[-0.38268343236508973, 0.92387953251128674, -0.0],
[-0.35073515920370746, 0.84674957815067731, -0.39999999999999997],
[-0.64807406984078597, 0.64807406984078608, -0.39999999999999997]],
[[-0.38268343236508973, 0.92387953251128674, -0.0],
[6.1230317691118863e-17, 1.0, -0.0], [5.6118513129149171e-17,
0.91651513899116799, -0.39999999999999997], [-0.35073515920370746,
0.84674957815067731, -0.39999999999999997]], [[6.1230317691118863e-17,
1.0, -0.0], [0.38268343236508984, 0.92387953251128674, -0.0],
[0.35073515920370757, 0.84674957815067731, -0.39999999999999997],
[5.6118513129149171e-17, 0.91651513899116799, -0.39999999999999997]],
[[0.38268343236508984, 0.92387953251128674, -0.0], [0.70710678118654757,
0.70710678118654746, -0.0], [0.64807406984078608, 0.64807406984078597,
-0.39999999999999997], [0.35073515920370757, 0.84674957815067731,
-0.39999999999999997]], [[0.70710678118654757, 0.70710678118654746,
-0.0], [0.92387953251128674, 0.38268343236508978, -0.0],
[0.84674957815067731, 0.35073515920370751, -0.39999999999999997],
[0.64807406984078608, 0.64807406984078597, -0.39999999999999997]],
[[0.92387953251128674, 0.38268343236508978, -0.0], [1.0, 0.0, -0.0],
[0.91651513899116799, 0.0, -0.39999999999999997], [0.84674957815067731,
0.35073515920370751, -0.39999999999999997]], [[1.0, 0.0, -0.0],
[0.92387953251128674, -0.38268343236508978, -0.0], [0.84674957815067731,
-0.35073515920370751, -0.39999999999999997], [0.91651513899116799, 0.0,
-0.39999999999999997]], [[0.92387953251128674, -0.38268343236508978,
-0.0], [0.70710678118654757, -0.70710678118654746, -0.0],
[0.64807406984078608, -0.64807406984078597, -0.39999999999999997],
[0.84674957815067731, -0.35073515920370751, -0.39999999999999997]],
[[0.70710678118654757, -0.70710678118654746, -0.0],
[0.38268343236508984, -0.92387953251128674, -0.0], [0.35073515920370757,
-0.84674957815067731, -0.39999999999999997], [0.64807406984078608,
-0.64807406984078597, -0.39999999999999997]], [[0.38268343236508984,
-0.92387953251128674, -0.0], [6.1230317691118863e-17, -1.0, -0.0],
[5.6118513129149171e-17, -0.91651513899116799, -0.39999999999999997],
[0.35073515920370757, -0.84674957815067731, -0.39999999999999997]],
[[6.1230317691118863e-17, -1.0, -0.0], [-0.38268343236508973,
-0.92387953251128674, -0.0], [-0.35073515920370746,
-0.84674957815067731, -0.39999999999999997], [5.6118513129149171e-17,
-0.91651513899116799, -0.39999999999999997]], [[-0.38268343236508973,
-0.92387953251128674, -0.0], [-0.70710678118654746,
-0.70710678118654757, -0.0], [-0.64807406984078597,
-0.64807406984078608, -0.39999999999999997], [-0.35073515920370746,
-0.84674957815067731, -0.39999999999999997]], [[-0.70710678118654746,
-0.70710678118654757, -0.0], [-0.92387953251128674,
-0.38268343236508989, -0.0], [-0.84674957815067731,
-0.35073515920370762, -0.39999999999999997], [-0.64807406984078597,
-0.64807406984078608, -0.39999999999999997]], [[-0.92387953251128674,
-0.38268343236508989, -0.0], [-1.0, -1.2246063538223773e-16, -0.0],
[-0.91651513899116799, -1.1223702625829834e-16, -0.39999999999999997],
[-0.84674957815067731, -0.35073515920370762, -0.39999999999999997]],
[[-0.91651513899116799, 1.1223702625829834e-16, -0.39999999999999997],
[-0.79372539331937719, 0.45825756949558394, -0.39999999999999997],
[-0.61846584384264902, 0.35707142142714238, -0.70000000000000007],
[-0.71414284285428486, 8.7454386289613274e-17, -0.70000000000000007]],
[[-0.79372539331937719, 0.45825756949558394, -0.39999999999999997],
[-0.45825756949558377, 0.79372539331937719, -0.39999999999999997],
[-0.35707142142714227, 0.61846584384264902, -0.70000000000000007],
[-0.61846584384264902, 0.35707142142714238, -0.70000000000000007]],
[[-0.45825756949558377, 0.79372539331937719, -0.39999999999999997],
[5.6118513129149171e-17, 0.91651513899116799, -0.39999999999999997],
[4.3727193144806637e-17, 0.71414284285428486, -0.70000000000000007],
[-0.35707142142714227, 0.61846584384264902, -0.70000000000000007]],
[[5.6118513129149171e-17, 0.91651513899116799, -0.39999999999999997],
[0.45825756949558411, 0.79372539331937708, -0.39999999999999997],
[0.35707142142714249, 0.61846584384264891, -0.70000000000000007],
[4.3727193144806637e-17, 0.71414284285428486, -0.70000000000000007]],
[[0.45825756949558411, 0.79372539331937708, -0.39999999999999997],
[0.79372539331937719, 0.45825756949558394, -0.39999999999999997],
[0.61846584384264902, 0.35707142142714238, -0.70000000000000007],
[0.35707142142714249, 0.61846584384264891, -0.70000000000000007]],
[[0.79372539331937719, 0.45825756949558394, -0.39999999999999997],
[0.91651513899116799, 0.0, -0.39999999999999997], [0.71414284285428486,
0.0, -0.70000000000000007], [0.61846584384264902, 0.35707142142714238,
-0.70000000000000007]], [[0.91651513899116799, 0.0,
-0.39999999999999997], [0.79372539331937719, -0.45825756949558394,
-0.39999999999999997], [0.61846584384264902, -0.35707142142714238,
-0.70000000000000007], [0.71414284285428486, 0.0,
-0.70000000000000007]], [[0.79372539331937719, -0.45825756949558394,
-0.39999999999999997], [0.45825756949558411, -0.79372539331937708,
-0.39999999999999997], [0.35707142142714249, -0.61846584384264891,
-0.70000000000000007], [0.61846584384264902, -0.35707142142714238,
-0.70000000000000007]], [[0.45825756949558411, -0.79372539331937708,
-0.39999999999999997], [5.6118513129149171e-17, -0.91651513899116799,
-0.39999999999999997], [4.3727193144806637e-17, -0.71414284285428486,
-0.70000000000000007], [0.35707142142714249, -0.61846584384264891,
-0.70000000000000007]], [[5.6118513129149171e-17, -0.91651513899116799,
-0.39999999999999997], [-0.45825756949558377, -0.79372539331937719,
-0.39999999999999997], [-0.35707142142714227, -0.61846584384264902,
-0.70000000000000007], [4.3727193144806637e-17, -0.71414284285428486,
-0.70000000000000007]], [[-0.45825756949558377, -0.79372539331937719,
-0.39999999999999997], [-0.79372539331937719, -0.45825756949558394,
-0.39999999999999997], [-0.61846584384264902, -0.35707142142714238,
-0.70000000000000007], [-0.35707142142714227, -0.61846584384264902,
-0.70000000000000007]], [[-0.79372539331937719, -0.45825756949558394,
-0.39999999999999997], [-0.91651513899116799, -1.1223702625829834e-16,
-0.39999999999999997], [-0.71414284285428486, -8.7454386289613274e-17,
-0.70000000000000007], [-0.61846584384264902, -0.35707142142714238,
-0.70000000000000007]], [[-0.71414284285428486, 8.7454386289613274e-17,
-0.70000000000000007], [-0.5049752469181038, 0.5049752469181038,
-0.70000000000000007], [-0.30822070014844877, 0.30822070014844882,
-0.90000000000000002], [-0.43588989435406733, 5.3379353419295565e-17,
-0.90000000000000002]], [[-0.5049752469181038, 0.5049752469181038,
-0.70000000000000007], [4.3727193144806637e-17, 0.71414284285428486,
-0.70000000000000007], [2.6689676709647783e-17, 0.43588989435406733,
-0.90000000000000002], [-0.30822070014844877, 0.30822070014844882,
-0.90000000000000002]], [[4.3727193144806637e-17, 0.71414284285428486,
-0.70000000000000007], [0.5049752469181038, 0.5049752469181038,
-0.70000000000000007], [0.30822070014844882, 0.30822070014844877,
-0.90000000000000002], [2.6689676709647783e-17, 0.43588989435406733,
-0.90000000000000002]], [[0.5049752469181038, 0.5049752469181038,
-0.70000000000000007], [0.71414284285428486, 0.0, -0.70000000000000007],
[0.43588989435406733, 0.0, -0.90000000000000002], [0.30822070014844882,
0.30822070014844877, -0.90000000000000002]], [[0.71414284285428486, 0.0,
-0.70000000000000007], [0.5049752469181038, -0.5049752469181038,
-0.70000000000000007], [0.30822070014844882, -0.30822070014844877,
-0.90000000000000002], [0.43588989435406733, 0.0,
-0.90000000000000002]], [[0.5049752469181038, -0.5049752469181038,
-0.70000000000000007], [4.3727193144806637e-17, -0.71414284285428486,
-0.70000000000000007], [2.6689676709647783e-17, -0.43588989435406733,
-0.90000000000000002], [0.30822070014844882, -0.30822070014844877,
-0.90000000000000002]], [[4.3727193144806637e-17, -0.71414284285428486,
-0.70000000000000007], [-0.5049752469181038, -0.5049752469181038,
-0.70000000000000007], [-0.30822070014844877, -0.30822070014844882,
-0.90000000000000002], [2.6689676709647783e-17, -0.43588989435406733,
-0.90000000000000002]], [[-0.5049752469181038, -0.5049752469181038,
-0.70000000000000007], [-0.71414284285428486, -8.7454386289613274e-17,
-0.70000000000000007], [-0.43588989435406733, -5.3379353419295565e-17,
-0.90000000000000002], [-0.30822070014844877, -0.30822070014844882,
-0.90000000000000002]], [[-0.43588989435406733, 5.3379353419295565e-17,
-0.90000000000000002], [2.6689676709647783e-17, 0.43588989435406733,
-0.90000000000000002], [3.7491518045553436e-33, 6.1230317691118863e-17,
-1.0], [-6.1230317691118863e-17, 7.4983036091106872e-33, -1.0]],
[[2.6689676709647783e-17, 0.43588989435406733, -0.90000000000000002],
[0.43588989435406733, 0.0, -0.90000000000000002],
[6.1230317691118863e-17, 0.0, -1.0], [3.7491518045553436e-33,
6.1230317691118863e-17, -1.0]], [[0.43588989435406733, 0.0,
-0.90000000000000002], [2.6689676709647783e-17, -0.43588989435406733,
-0.90000000000000002], [3.7491518045553436e-33, -6.1230317691118863e-17,
-1.0], [6.1230317691118863e-17, 0.0, -1.0]], [[2.6689676709647783e-17,
-0.43588989435406733, -0.90000000000000002], [-0.43588989435406733,
-5.3379353419295565e-17, -0.90000000000000002],
[-6.1230317691118863e-17, -7.4983036091106872e-33, -1.0],
[3.7491518045553436e-33, -6.1230317691118863e-17, -1.0]]]

// Constructor
Pipe = function() {
  this.t = []
  this.tp = []
  }
Pipe.prototype = {
  // ModelView matrix.
  mv: null,
  // Projection matrix.
  p: null,
  // Transformed Primitives
  tp: null,
  // Temporary Vertex List
  t: null,
  // Rasterizer
  r: function(){},
  // Set the ModelView matrix.  Matrix argument is referenced, not
  // copied, and it should not be modified whilst it is the ModelView
  // matrix.
  mvMatrix: function(m) {
    this.mv = m
  },
  // Set the Projection matrix.  Matrix argument is referenced, not
  // copied, and it should not be modified whilst it is the ModelView
  // matrix.
  pMatrix: function(m) {
    this.p = m
  },
  // Begin/End vertex lists, etc.
  begin: function(mode) {
    // assert this.mode == ''
    this.mode = mode
  },
  end: function() {
    // assert this.mode != ''
    this.mode == ''
  },
  vertex: function(v) {
    // assert this.mode != ''
    if(v.length == 2) {
      v = [v[0], v[1], 0, 1]
    } else if(v.length == 3) {
      v=[v[0], v[1], v[2], 1]
    }
    this.t.push(this.xform(v))
    if(this.mode === 'triangles' && this.t.length == 3) {
      this.triangle(this.t)
      this.t = []
    }
    if(this.mode === 'quads' && this.t.length == 4) {
      this.quad(this.t)
      this.t = []
    }
  },
  triangle: function(t) {
    return this.primitive(t)
  },
  quad: function(q) {
    return this.primitive(q)
  },
  primitive: function(t) {
    this.tp.push(t)
    // front face cull, see [GL1.2.1] equation 2.7 page 48
    var a = 0
    var i
    for(i=0;i<t.length;++i) {
      var s = i + 1
      if(s >= t.length) {
        s = 0
      }
      a += t[i][0]*t[s][1]
      a -= t[s][0]*t[i][1]
    }
    // Interpret a > 0 as front face.  See FrontFace [GL1.2.1] page 48,
    // which we do not implement.
    // There is no cull control.  Effectively culling is enabled and
    // mode is BACK (see [GL1.2.1] section 3.5.1).
    // Only front facing polygons will be kept.
    if(a <= 0) {
      // Culled
      return
    }
    this.r(t)
  },
  xform: function(v) {
    // Model View transform
    v = M44V(this.mv, v)
    // Lighting etc (there is none)
    // Projection transform
    v = M44V(this.p, v)
    // Perspective division, "divide by w"
    v = Vscale(v, 1/v[3])
    return v
  },
  // A rasterizer is a function that takes fully transformed triangles
  // (er, or quadrilaterals) and rasterizes them into pixels.
  // Although some "rasterizers" might not actually "rasterize".
  rasterizer: function(r) {
    this.r = r
  },

  eol: null
}

// Matrix vector conventions
// Generally vectors wil be 3- or 4-vectors represented as arrays.
// Generally vectors will be assumed to be column vectors.  Often
// 3-vectors are assumed to be homogeneous co-ordinates (by assuming the
// 4th coordinate of 0 or 1 as appropriate).
// Matrices are generally 4x4, 16 entries.  Generally represented as an
// 16 element array with entried 0 to 15.  Store row major, so that
// row i, column j is at index 4*i+j.

// Convert M to a "pretty" string.
// Actually not very pretty, but one matrix row per line.
function prettyM(m)
{
  var s = ''
  var i
  for(i=0;i<4;++i) {
    s += m.slice(i*4,i*4+4) + '\n'
  }
  return s
}

// Convert primitive (internal) to pretty string.
function prettyP(p)
{
  var s = ''
  if(p.length == 3) {
    s += 'triangle '
  }
  s += '['
  var i
  for(i=0;i<p.length;++i) {
    s += '[' + p[i] + ']'
  }
  s += ']'
  return s
}

// Multiply a 4x4 matrix by column vector V, producing another column
// vector.  3-vectors are assumed to have a 4th coord of 1.
function M44V(m, v) {
  if(v.length < 4) {
    v = [v[0], v[1], v[2], 1]
  }
  var row
  var r = []
  for(row=0; row<4; ++row) {
    var o = row*4
    r[row] = m[o+0]*v[0] +
             m[o+1]*v[1] +
             m[o+2]*v[2] +
             m[o+3]*v[3]
  }
  return r
}

// Multiply 2 4x4 matrices
function M44M44(m, n)
{
  var row,col,i
  var r = []
  // Loop over the result matrix
  for(row=0;row<4;++row) {
    for(col=0;col<4;++col) {
      var e = 0
      for(i=0;i<4;++i) {
        e += m[row*4+i]*n[i*4+col]
      }
      r.push(e)
    }
  }
  return r
}

// Return the length of the 3-vector.
function Vlength(v)
{
  return Math.sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2])
}

// Scale a 3-vector by multiplying it by scalar s.
function Vscale(v, s)
{
  return [v[0]*s, v[1]*s, v[2]*s]
}

// Normalise a vector by scaling it by the reciprocal of its length
function Vnorm(v)
{
  var l = Vlength(v)
  return Vscale(v, 1/l)
}

// cross product
function VcrossV(u, v)
{
  return [u[1]*v[2]-u[2]*v[1],
          u[2]*v[0]-u[0]*v[2],
          u[0]*v[1]-u[1]*v[0]]
}

// See [GL1.1] 2.10.2 (page 31)
function frustum(left, right, bottom, top, near, far)
{
  var A = (right + left) / (right - left)
  var B = (top + bottom) / (top - bottom)
  var C = -(far + near) / (far - near)
  var D = -2*far*near / (far - near)
  return [2*near/(right-left), 0, A, 0,
          0, 2*near/(top-bottom), B, 0,
          0, 0,                   C, D,
          0, 0,                  -1, 0]
}

// See
// http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/glu/lookat.html
// (plus a bit of guess work)
// eye, centre, up are 3-vectors.
function lookat(eye, centre, up)
{
  var f = [centre[0]-eye[0], centre[1]-eye[1], centre[2]-eye[2]]
  f = Vnorm(f)
  up = Vnorm(up)
  var s = VcrossV(f, up)
  var u = VcrossV(s, f)
  var M = [s[0],   s[1],  s[2], 0,
           u[0],   u[1],  u[2], 0,
           -f[0], -f[1], -f[2], 0,
           0,         0,     0, 1]
  var T = [1, 0, 0, -eye[0],
           0, 1, 0, -eye[1],
           0, 0, 1, -eye[2],
           0, 0, 0, 1]
  return M44M44(M,T)
}

// Returns a canvasrasterizer that will use the canvas c.
// For no particularly good reason it also sets the transform on c
function canvasrasterizer(c) {
  var c2d = c.getContext('2d')
  c2d.scale(c.width/2, c.height/2)
  c2d.translate(1,1)
  c2d.scale(1, -1)
  return function (p) {
    var i
    // Assume p is single primitive.
    c2d.beginPath()
    c2d.moveTo(p[0][0], p[0][1])
    for(i=1;i<p.length;++i) {
      c2d.lineTo(p[i][0], p[i][1])
    }
    c2d.closePath()
    c2d.stroke()
    c2d.fill()
  }
}

zap=function(){}
if(!this.document) {
  zap = print
}

function ipl() {
  var c = document.getElementById('html-canvas')
  var d = document.getElementById('polar-canvas')
  var a
  map(function(a) {
      var c2d = a.getContext('2d')
      c2d.fillStyle = '#ff00ff'
      c2d.strokeStyle = '#000000'
      c2d.lineWidth = 0.002
    }, [c, d])

  g = new Pipe()
  g.mvMatrix(lookat([2,-9,2],[0,0,0],[0,0,1]))
  g.pMatrix(frustum(-0.2,0.2,-0.2,0.2,1,100))
  g.rasterizer(canvasrasterizer(c))

  h = new Pipe()
  h.mvMatrix(lookat([0,0,9],[0,0,0],[0,1,0]))
  h.pMatrix(frustum(-0.2,0.2,-0.2,0.2,1,100))
  h.rasterizer(canvasrasterizer(d))

  g.begin('quads')
  h.begin('quads')
  var i,j
  for(j=0;j<grid.length;++j) {
    for(i=0;i<4;++i) {
      g.vertex(grid[j][i])
      h.vertex(grid[j][i])
    }
  }
  g.end()
  h.end()
}
